#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "NUC900_reg.h"
#include "wblib.h"
#include "NUC900_gnand.h"
#include "NUC900_sic.h"

#define FMI_TEST_SIZE	512 * 128
__align(4096) UINT8 fmiFlash_Buf[FMI_TEST_SIZE];
__align(4096) UINT8 fmiReadBackBuffer[FMI_TEST_SIZE];

unsigned char *pSrc_fmi, *pDst_fmi;

extern int  get_item_number(int max);
extern int diag_sd0(void);
extern int diag_sd1(void);
extern int diag_nand(void);
extern int diag_ms0(void);
extern int diag_ms1(void);
extern int diag_sd0_cd(void);
extern int diag_sd1_cd(void);
extern int diag_ms0_cd(void);
extern int diag_ms1_cd(void);

struct fmi_test_list
{
	char  	name[32];
	int 	(*diag_func)(void);
};

struct fmi_test_list  test_items_fmi_910[] =
{
	"",					NULL,
	"(1)  SD Port 0         ",	diag_sd0,
	"(2)  SD Port 1         ",	diag_sd1,
	"(3)  NAND              ",	diag_nand,
	"(4)  MS Port 0         ",	diag_ms0,
	"(5)  MS Port 1         ",	diag_ms1,
	"(6)  SD Port 0 card detection",	diag_sd0_cd,
	"(7)  SD Port 1 card detection",	diag_sd1_cd,
	"(8)  MS Port 0 card detection",	diag_ms0_cd,
	"(9)  MS Port 1 card detection",	diag_ms1_cd,
	"(10) Exit              ",	NULL
};


struct fmi_test_list  test_items_fmi_920[] =
{
	"",					NULL,
	"(1)  SD Port 1         ",	diag_sd1,
	"(2)  MS Port 1         ",	diag_ms1,
	"(3)  SD Port 1 card detection",	diag_sd1_cd,
	"(4)  MS Port 1 card detection",	diag_ms1_cd,
	"(5) Exit              ",	NULL
};


unsigned int volatile SD0_Flag=0, SD1_Flag=0;
unsigned int volatile MS0_Flag=0, MS1_Flag=0;
void SD0_InsertDevice()
{
	SD0_Flag = 1;
}
void SD1_InsertDevice()
{
	SD1_Flag = 1;
}

void SD0_RemoveDevice()
{
	SD0_Flag = 0;
}


void SD1_RemoveDevice()
{
	SD0_Flag = 0;
}

void MS0_InsertDevice()
{
	MS0_Flag = 1;
}

void MS0_RemoveDevice()
{
	MS0_Flag = 0;
}

void MS1_InsertDevice()
{
	MS1_Flag = 1;
}

void MS1_RemoveDevice()
{
	MS1_Flag = 0;
}

static int  _fmi_inited = 0;

int diag_fmi()
{
	unsigned int volatile i, data;
	struct fmi_test_list	*test_items;
	INT					item_len, idx;

	if (_fmi_inited == 0)
	{
		// set FMI reference clock
		sicIoctl(SIC_SET_CLOCK, 200000, 0, 0);
		sicIoctl(SIC_SET_CALLBACK, FMI_SD_CARD0, (INT32)SD0_RemoveDevice, (INT32)SD0_InsertDevice);
		sicIoctl(SIC_SET_CALLBACK, FMI_SD_CARD1, (INT32)SD1_RemoveDevice, (INT32)SD1_InsertDevice);
		sicIoctl(SIC_SET_CALLBACK, FMI_MS_CARD0, (INT32)MS0_RemoveDevice, (INT32)MS0_InsertDevice);
		sicIoctl(SIC_SET_CALLBACK, FMI_MS_CARD1, (INT32)MS1_RemoveDevice, (INT32)MS1_InsertDevice);
		pSrc_fmi = (UINT8 *)((UINT32)fmiFlash_Buf | 0x80000000);
		pDst_fmi = (UINT8 *)((UINT32)fmiReadBackBuffer | 0x80000000);

		data = 0;
		for (i=0; i<FMI_TEST_SIZE; i++)
		{
			*(pSrc_fmi+i) = (data + i) & 0xff;
			if (((i+1) % 0xff) == 0)
				data++;
		}

		// initial FMI
		sicOpen();
	}

	if ((inpw(REG_PDID) & 0xfff) == 0x910)
	{
		// test item
		test_items = test_items_fmi_910;
		item_len   = sizeof(test_items_fmi_910) / sizeof(struct fmi_test_list);

		while (1)
		{
			sysprintf("\n\n\n\n");

			/*--------------------------------------------------------------------------------*/
			/* print list test items                                                          */
			/*--------------------------------------------------------------------------------*/
			sysprintf("+----------------------------------+\n");
			sysprintf("|  NUC%x FMI test items           |\n", inpw(REG_PDID) & 0xfff);
			sysprintf("+----------------------------------+\n");
			for (i = 1; i < 6; i++)
				sysprintf("| %s          |\n", test_items[i].name);
			for (i = 6; i < item_len-1; i++)
				sysprintf("| %s    |\n", test_items[i].name);
			sysprintf("| %s          |\n", test_items[10].name);
			sysprintf("+----------------------------------+\n");
		
			sysprintf("\nPlease select the test item: ");
			idx = get_item_number(item_len);
			if (idx < 0)
				continue;
			if (idx == 10)
				break;

			sysprintf("\nSelect test item %d\n\n\n", idx);
		
			if (test_items[idx].diag_func != NULL)
				test_items[idx].diag_func();
			else
			{
				sysprintf("\n\n\n%s diagnostic code not available!\n", test_items[idx].name);
			}
			sysprintf("\n\nPress any key to continue...\n");
			sysGetChar();
		}
	}
	else if ((inpw(REG_PDID) & 0xfff) == 0x920)
	{
		// test item
		test_items = test_items_fmi_920;
		item_len   = sizeof(test_items_fmi_920) / sizeof(struct fmi_test_list);

		while (1)
		{
			sysprintf("\n\n\n\n");

			/*--------------------------------------------------------------------------------*/
			/* print list test items                                                          */
			/*--------------------------------------------------------------------------------*/
			sysprintf("+----------------------------------+\n");
			sysprintf("|  NUC%x FMI test items           |\n", inpw(REG_PDID) & 0xfff);
			sysprintf("+----------------------------------+\n");
			for (i = 1; i < 3; i++)
				sysprintf("| %s          |\n", test_items[i].name);
			for (i = 3; i < item_len-1; i++)
				sysprintf("| %s    |\n", test_items[i].name);
			sysprintf("| %s          |\n", test_items[5].name);
			sysprintf("+----------------------------------+\n");
		
			sysprintf("\nPlease select the test item: ");
			idx = get_item_number(item_len);
			if (idx < 0)
				continue;
			if (idx == 5)
				break;

			sysprintf("\nSelect test item %d\n\n\n", idx);
		
			if (test_items[idx].diag_func != NULL)
				test_items[idx].diag_func();
			else
			{
				sysprintf("\n\n\n%s diagnostic code not available!\n", test_items[idx].name);
			}
			sysprintf("\n\nPress any key to continue...\n");
			sysGetChar();
		}
	}
	return 0;
}

int diag_sd0()
{
	BOOL err_flag=0;
	int volatile i, sector;
	int volatile btime, status;

	sysprintf("\nTest SD port 0 ...\n");
	sysprintf("Please check the SD port 0 and MS port 0 switch...\n");
	sysprintf("SD0 should turn ON and MS0 switch turn OFF...\n");
	sysprintf("\n\nPress any key to begin test...\n");
	sysGetChar();

	/* begin test */
	memset(pDst_fmi, 0, FMI_TEST_SIZE);

	sector = sicSdOpen(0);
	if (sector < 0)
	{
		sysprintf("sd0 init fail [%d]\n", sector);
		err_flag = 1;
		goto _exit_sd0_;
	}	
	sysprintf("total sectors [%d]\n", sector);

	sysprintf("SD write\n");
	btime = sysGetTicks(TIMER0);
	status = sicSdWrite(0, 9000, FMI_TEST_SIZE/512, (unsigned int)pSrc_fmi);
	btime = sysGetTicks(TIMER0) - btime;
	if (btime == 0) btime = 1;

	sysprintf("[%d] SD0 write speed %d KB/sec [%d]\n", status, (FMI_TEST_SIZE * 100) / (btime * 1024), btime);

	sysprintf("SD read\n");
	btime = sysGetTicks(TIMER0);
	status = sicSdRead(0, 9000, FMI_TEST_SIZE/512, (unsigned int)pDst_fmi);
	btime = sysGetTicks(TIMER0) - btime;
	if (btime == 0) btime = 1;

	sysprintf("[%d] SD0 read speed %d KB/sec\n", status, (FMI_TEST_SIZE * 100) / (btime * 1024));

	sysprintf("compare SD0 ...\n");
	for (i=0; i<FMI_TEST_SIZE; i++)
	{
		if (*(pSrc_fmi+i) != *(pDst_fmi+i))
		{
			sysprintf("error!! <%d> Src[%x], Dst[%x]\n", i, *(pSrc_fmi+i), *(pDst_fmi+i));
			err_flag = 1;
			break;
		}
	}

_exit_sd0_:
	if (err_flag == 1)
		sysprintf("SD0 test failed!!!\n");
	else
		sysprintf("SD0 test OK!!!\n");
	return 0;
}

int diag_sd1()
{
	BOOL err_flag=0;
	int volatile i, sector;
	int volatile btime, status;

	sysprintf("\nTest SD port 1 ...\n");
	sysprintf("Please check the SD port 1 and MS port 1 switch...\n");
	sysprintf("SD1 should turn ON and MS1 switch turn OFF...\n");
	sysprintf("\n\nPress any key to begin test...\n");
	sysGetChar();

	/* begin test */
	memset(pDst_fmi, 0, FMI_TEST_SIZE);

	sector = sicSdOpen(1);
	if (sector < 0)
	{
		sysprintf("sd1 init fail [%d]\n", sector);
		err_flag = 1;
		goto _exit_sd1_;
	}	
	sysprintf("total sectors [%d]\n", sector);

	sysprintf("SD write\n");
	btime = sysGetTicks(TIMER0);
	status = sicSdWrite(1, 6000, FMI_TEST_SIZE/512, (unsigned int)pSrc_fmi);
	btime = sysGetTicks(TIMER0) - btime;
	if (btime == 0) btime = 1;

	sysprintf("[%d] SD1 write speed %d KB/sec [%d]\n", status, (FMI_TEST_SIZE * 100) / (btime * 1024), btime);

	sysprintf("SD read\n");
	btime = sysGetTicks(TIMER0);
	status = sicSdRead(1, 6000, FMI_TEST_SIZE/512, (unsigned int)pDst_fmi);
	btime = sysGetTicks(TIMER0) - btime;
	if (btime == 0) btime = 1;

	sysprintf("[%d] SD1 read speed %d KB/sec\n", status, (FMI_TEST_SIZE * 100) / (btime * 1024));

	sysprintf("compare SD1 ...\n");
	for (i=0; i<FMI_TEST_SIZE; i++)
	{
		if (*(pSrc_fmi+i) != *(pDst_fmi+i))
		{
			sysprintf("error!! <%d> Src[%x], Dst[%x]\n", i, *(pSrc_fmi+i), *(pDst_fmi+i));
			err_flag = 1;
			break;
		}
	}

_exit_sd1_:
	if (err_flag == 1)
		sysprintf("SD1 test failed!!!\n");
	else
		sysprintf("SD1 test OK!!!\n");
	return 0;
}


NDRV_T _nandDiskDriver0 = 
{
	nandInit0,
	nandpread0,
	nandpwrite0,
	nand_is_page_dirty0,
	nand_is_valid_block0,
	nand_ioctl,
	nand_block_erase0,
	nand_chip_erase0,
	0
};

int diag_nand()
{
	BOOL err_flag=0;
	int volatile i, totalSec;
	int volatile btime, status;
	NDISK_T *ptNDisk;
	
	sysprintf("\nTest NAND ...\n");
	sysprintf("Please check the NAND switch...\n");
	sysprintf("It should turn ON...\n");
	sysprintf("\n\nPress any key to begin test...\n");
	sysGetChar();
	
	outpw(REG_MFSEL, (inpw(REG_MFSEL) & 0xFFFFFFF3) | 0x04);

	ptNDisk = (NDISK_T *)malloc(sizeof(NDISK_T));
	if (ptNDisk == NULL)
		sysprintf("malloc error!!\n");
	
	sysprintf("GNAND Init %x\n", GNAND_InitNAND(&_nandDiskDriver0, ptNDisk, TRUE));

	totalSec = FMI_TEST_SIZE/512;
	sysprintf("NAND write\n");
	btime = sysGetTicks(TIMER0);
	status = GNAND_write(ptNDisk, 0, FMI_TEST_SIZE/512, pSrc_fmi);
	btime = sysGetTicks(TIMER0) - btime;
	if (btime == 0) btime = 1;

	sysprintf("[%x] NAND write speed %d KB/sec\n", status, (FMI_TEST_SIZE * 100) / (btime * 1024));

	sysprintf("NAND read\n");
	btime = sysGetTicks(TIMER0);
	status = GNAND_read(ptNDisk, 0, FMI_TEST_SIZE/512, pDst_fmi);
	btime = sysGetTicks(TIMER0) - btime;
	if (btime == 0) btime = 1;

	sysprintf("[%x] NAND read speed %d KB/sec\n", status, (FMI_TEST_SIZE * 100) / (btime * 1024));

	sysprintf("compare\n");
	for (i=0; i<FMI_TEST_SIZE; i++)
	{
		if (*(pSrc_fmi+i) != *(pDst_fmi+i))
		{
			sysprintf("error!! <%x> Src[%x], Dst[%x]\n", i, *(pSrc_fmi+i), *(pDst_fmi+i));
			break;
		}
	}

	if (err_flag == 1)
		sysprintf("NAND test failed!!!\n");
	else
		sysprintf("NAND test OK!!!\n");

	outpw(REG_MFSEL, (inpw(REG_MFSEL) & 0xFFFFFFF3) | 0x0C);

	return 0;
}


int diag_ms0()
{
	BOOL err_flag=0;
	int volatile i, sector;
	int volatile btime, status;

	sysprintf("\nTest MS port 0 ...\n");
	sysprintf("Please check the MS port 0 and SD port 0 switch...\n");
	sysprintf("MS0 should turn ON and SD0 switch turn OFF...\n");
	sysprintf("\n\nPress any key to begin test...\n");
	sysGetChar();

	/* begin test */
	memset(pDst_fmi, 0, FMI_TEST_SIZE);

	sector = sicMsOpen(0);
	if (sector < 0)
	{
		sysprintf("MS0 init fail [%d]\n", sector);
		err_flag = 1;
		goto _exit_ms0_;
	}	
	sysprintf("total sectors [%d]\n", sector);

	sysprintf("MS write\n");
	btime = sysGetTicks(TIMER0);
	status = sicMsWrite(0, 20000, FMI_TEST_SIZE/512, (unsigned int)pSrc_fmi);
	btime = sysGetTicks(TIMER0) - btime;
	if (btime == 0) btime = 1;

	sysprintf("[%d] MS write speed %d KB/sec\n", status, (FMI_TEST_SIZE * 100) / (btime * 1024));

	sysprintf("MS read\n");
	btime = sysGetTicks(TIMER0);
	status = sicMsRead(0, 20000, FMI_TEST_SIZE/512, (unsigned int)pDst_fmi);
	btime = sysGetTicks(TIMER0) - btime;
	if (btime == 0) btime = 1;

	sysprintf("[%d] MS read speed %d KB/sec\n", status, (FMI_TEST_SIZE * 100) / (btime * 1024));


	sysprintf("compare\n");
	for (i=0; i<FMI_TEST_SIZE; i++)
	{
		if (*(pSrc_fmi+i) != *(pDst_fmi+i))
		{
			sysprintf("error!! <%d> Src[%x], Dst[%x]\n", i, *(pSrc_fmi+i), *(pDst_fmi+i));
			err_flag = 1;
			break;
		}
	}

_exit_ms0_:
	if (err_flag == 1)
		sysprintf("MS0 test failed!!!\n");
	else
		sysprintf("MS0 test OK!!!\n");

	return 0;
}

int diag_ms1()
{
	BOOL err_flag=0;
	int volatile i, sector;
	int volatile btime, status;

	sysprintf("\nTest MS port 1 ...\n");
	sysprintf("Please check the MS port 1 and SD port 1 switch...\n");
	sysprintf("MS1 should turn ON and SD1 switch turn OFF...\n");
	sysprintf("\n\nPress any key to begin test...\n");
	sysGetChar();

	/* begin test */
	memset(pDst_fmi, 0, FMI_TEST_SIZE);

	sector = sicMsOpen(1);
	if (sector < 0)
	{
		sysprintf("ms1 init fail [%d]\n", sector);
		err_flag = 1;
		goto _exit_ms1_;
	}	
	sysprintf("total sectors [%d]\n", sector);

	sysprintf("MS write\n");
	btime = sysGetTicks(TIMER0);
	status = sicMsWrite(1, 10000, FMI_TEST_SIZE/512, (unsigned int)pSrc_fmi);
	btime = sysGetTicks(TIMER0) - btime;
	if (btime == 0) btime = 1;

	sysprintf("[%d] MS write speed %d KB/sec\n", status, (FMI_TEST_SIZE * 100) / (btime * 1024));

	sysprintf("MS read\n");
	btime = sysGetTicks(TIMER0);
	status = sicMsRead(1, 10000, FMI_TEST_SIZE/512, (unsigned int)pDst_fmi);
	btime = sysGetTicks(TIMER0) - btime;
	if (btime == 0) btime = 1;

	sysprintf("[%d] MS read speed %d KB/sec\n", status, (FMI_TEST_SIZE * 100) / (btime * 1024));


	sysprintf("compare\n");
	for (i=0; i<FMI_TEST_SIZE; i++)
	{
		if (*(pSrc_fmi+i) != *(pDst_fmi+i))
		{
			sysprintf("error!! <%d> Src[%x], Dst[%x]\n", i, *(pSrc_fmi+i), *(pDst_fmi+i));
			err_flag = 1;
			break;
		}
	}

_exit_ms1_:
	if (err_flag == 1)
		sysprintf("MS1 test failed!!!\n");
	else
		sysprintf("MS1 test OK!!!\n");

	return 0;
}

int diag_sd0_cd()
{
	int status = 0, remove_count=0, insert_count=0;
	unsigned char *ptr;

	sysprintf("\nTest SD port 0 ...\n");
	sysprintf("Please check the SD port 0 and MS port 0 switch...\n");
	sysprintf("SD0 should turn ON and MS0 switch turn OFF...\n");
	sysprintf("\nIt will test 2 times insert/remove !!!");
	sysprintf("\n\nPress any key to begin test...\n");
	sysGetChar();

	ptr = (unsigned char *)0x80100000;
	status = sicSdOpen(0);
	if (status == FMI_NO_SD_CARD)
	{
		sysprintf("No SD0 Card!!\n");
		remove_count++;
		while(1)
		{
			if (SD0_Flag == 1)
			{
				status = sicSdOpen(0);
				sysprintf("SD0 card insert [%d]!!\n", status);
				insert_count++;
				break;
			}
		}
	}
	else
	{
		sysprintf("SD0 Card insert!!\n");
		insert_count++;
		SD0_Flag = 1;
	}

	while(1)
	{
		status = sicSdRead(0,  1000, 512, (unsigned int)ptr);
		if (status == FMI_NO_SD_CARD)
		{
			sysprintf("SD0 card remove!!\n");
			remove_count++;
			while(1)
			{
				if (SD0_Flag == 1)
				{
					status = sicSdOpen(0);
					sysprintf("SD0 card insert [%d]!!\n", status);
					insert_count++;
					break;
				}
			}
		}

		if ((remove_count>1) || (insert_count>1))
		{
			sysprintf("Please any key to exit the test ...\n");
			sysGetChar();
			break;
		}
	}

	return 0;
}

int diag_sd1_cd()
{
	int status = 0, remove_count=0, insert_count=0;
	unsigned char *ptr;

	sysprintf("\nTest SD port 1 ...\n");
	sysprintf("Please check the SD port 1 and MS port 1 switch...\n");
	sysprintf("SD1 should turn ON and MS1 switch turn OFF...\n");
	sysprintf("\nIt will test 2 times insert/remove !!!");
	sysprintf("\n\nPress any key to begin test...\n");
	sysGetChar();

	ptr = (unsigned char *)0x80100000;
	status = sicSdOpen(1);
	if (status == FMI_NO_SD_CARD)
	{
		sysprintf("No SD1 Card!!\n");
		remove_count++;
		while(1)
		{
			if (SD1_Flag == 1)
			{
				status = sicSdOpen(1);
				sysprintf("SD1 card insert [%d]!!\n", status);
				insert_count++;
				break;
			}
		}
	}
	else
	{
		sysprintf("SD1 Card insert!!\n");
		insert_count++;
		SD1_Flag = 1;
	}

	while(1)
	{
		status = sicSdRead(1,  1000, 512, (unsigned int)ptr);
		if (status == FMI_NO_SD_CARD)
		{
			sysprintf("SD1 card remove!!\n");
			remove_count++;
			while(1)
			{
				if (SD1_Flag == 1)
				{
					status = sicSdOpen(1);
					sysprintf("SD1 card insert [%d]!!\n", status);
					insert_count++;
					break;
				}
			}
		}

		if ((remove_count>1) || (insert_count>1))
		{
			sysprintf("Please any key to exit the test ...\n");
			sysGetChar();
			break;
		}
	}

	return 0;
}

int diag_ms0_cd()
{
	int status = 0, remove_count=0, insert_count=0;
	unsigned char *ptr;

	sysprintf("\nTest MS port 0 ...\n");
	sysprintf("Please check the MS port 0 and SD port 0 switch...\n");
	sysprintf("MS0 should turn ON and SD0 switch turn OFF...\n");
	sysprintf("\nIt will test 2 times insert/remove !!!");
	sysprintf("\n\nPress any key to begin test...\n");
	sysGetChar();

	ptr = (unsigned char *)0x80100000;
	status = sicMsOpen(0);
	if (status == FMI_NO_MS_CARD)
	{
		sysprintf("No MS0 Card!!\n");
		remove_count++;
		while(1)
		{
			if (MS0_Flag == 1)
			{
				status = sicMsOpen(0);
				sysprintf("MS0 card insert [%d]!!\n", status);
				insert_count++;
				break;
			}
		}
	}
	else
	{
		sysprintf("MS0 Card insert!!\n");
		insert_count++;
		MS0_Flag = 1;
	}

	while(1)
	{
		status = sicMsRead(0,  1000, 512, (unsigned int)ptr);
		if (status == FMI_NO_MS_CARD)
		{
			sysprintf("MS0 card remove!!\n");
			remove_count++;
			while(1)
			{
				if (MS0_Flag == 1)
				{
					status = sicMsOpen(0);
					sysprintf("MS0 card insert [%d]!!\n", status);
					insert_count++;
					break;
				}
			}
		}

		if ((remove_count>1) || (insert_count>1))
		{
			sysprintf("Please any key to exit the test ...\n");
			sysGetChar();
			break;
		}
	}

	return 0;
}

int diag_ms1_cd()
{
	int status = 0, remove_count=0, insert_count=0;
	unsigned char *ptr;

	sysprintf("\nTest MS port 1 ...\n");
	sysprintf("Please check the MS port 1 and SD port 1 switch...\n");
	sysprintf("MS1 should turn ON and SD1 switch turn OFF...\n");
	sysprintf("\nIt will test 2 times insert/remove !!!");
	sysprintf("\n\nPress any key to begin test...\n");
	sysGetChar();

	ptr = (unsigned char *)0x80100000;
	status = sicMsOpen(1);
	if (status == FMI_NO_MS_CARD)
	{
		sysprintf("No MS1 Card!!\n");
		remove_count++;
		while(1)
		{
			if (MS1_Flag == 1)
			{
				status = sicMsOpen(1);
				sysprintf("MS1 card insert [%d]!!\n", status);
				insert_count++;
				break;
			}
		}
	}
	else
	{
		sysprintf("MS1 Card insert!!\n");
		insert_count++;
		MS1_Flag = 1;
	}

	while(1)
	{
		status = sicMsRead(1,  1000, 512, (unsigned int)ptr);
		if (status == FMI_NO_MS_CARD)
		{
			sysprintf("MS1 card remove!!\n");
			remove_count++;
			while(1)
			{
				if (MS1_Flag == 1)
				{
					status = sicMsOpen(1);
					sysprintf("MS1 card insert [%d]!!\n", status);
					insert_count++;
					break;
				}
			}
		}

		if ((remove_count>1) || (insert_count>1))
		{
			sysprintf("Please any key to exit the test ...\n");
			sysGetChar();
			break;
		}
	}

	return 0;
}


